from django.shortcuts import render
from django.views import View
from django import http
from apps.goods.models import SKU
from django_redis import get_redis_connection
import json, base64, pickle


# Create your views here.


class CartsView(View):
    """购物车:增删改查"""
    def post(self, request):
        """新增购物车逻辑"""
        # 接收参数
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')
        count = json_dict.get('count')
        # 如果前端没有传递selected参数,我们给个默认值True,默认勾选
        selected = json_dict.get('selected', True)

        # 校验参数
        if not all([sku_id, count]):
            return http.JsonResponse({'code': 400, 'errmsg': '比传参数有误'})
        try:
            SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return http.JsonResponse({'code': 400, 'errmsg': 'sku_id有误'})
        try:
            # 使用int类型强转count,如果count不是数字,自动抛出异常
            count = int(count)
        except BaseException:
            return http.JsonResponse({'code': 400, 'errmsg': 'count有误'})
        if selected:
            if not isinstance(selected, bool):
                return http.JsonResponse({'code': 400, 'errmsg': 'selected有误'})

        # 实现核心逻辑:登录用户/未登录和用户
        # 判断用户登录状态
        # 已登录-->新增redis购物车
        if request.user.is_authenticated:
            # 创建连接到redis5号库的对象
            redis_conn = get_redis_connection('carts')
            pl = redis_conn.pipeline()

            user_id = request.user.id
            # 操作hash,增量存储sku_id和count
            # redis_conn.hincrby('carts_%s' % request.user.id, sku_id, count)
            pl.hincrby('carts_%s' % user_id, sku_id, count)
            # 操作set,如果selected为True,将sku_id添加到set
            if selected:
                # redis_conn.sadd('selected_%s' % request.user.id, sku_id)
                pl.sadd('selected_%s' % user_id, sku_id)
            pl.execute()

            # 返回响应
            return http.JsonResponse({'code': 400, 'errmsg': 'OK'})
       # 未登录-->新增cookie购物车
        else:
            # 1.从cookie中读取购物车字典
            # 从cookie中读取之前保串存的购物车密文字符串
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                # 将密文字符转成bytes类型的密文字符串
                cart_str_bytes = cart_str.encode()
                # 将bytes类型的米文字符串使用base64解码成bytes类型字典
                cart_dict_bytes = base64.b64decode(cart_str_bytes)
                # 将bytes类型的字典使用pickle反序列化为python字典
                cart_dict = pickle.loads(cart_dict_bytes)
            else:
                cart_dict = {}

            # 2. 添加购物车数据到购物车字典
            if sku_id in cart_dict:
            # 如果要添加的商品已存在,累加
                origin_count = cart_dict[sku_id]['count']
                count += origin_count
            # 不存在,新增
            cart_dict[sku_id] = {
                "count": count,
                "selected": selected,
            }

            # 3. 购物车字典转字符串并写入到cookie
            # 使用pickle将cart_dict序列化为bytes类型字典
            cart_dict_bytes = pickle.dumps(cart_dict)
            # 使用base64将bytes类型的字典编码为bytes类型的密文字符串
            cart_str_bytes = base64.b64encode(cart_dict_bytes)
            # 将bytes类型的密文字符串转普通字符串
            cookie_cart_str = cart_str_bytes.decode()
            # 将密文字符串写入cookie
            response = http.JsonResponse({'code': 0, 'errmsg': 'OK'})
            response.set_cookie('carts', cookie_cart_str)

        # 响应结果
        return response

    def get(self, request):
        """实现查询购物车"""
        # 判断用户是否已登录
        if request.user.is_authenticated:
            # 已登录,查询redis购物车
            user_id = request.user.id
            redis_conn = get_redis_connection('carts')
            # 查询hash中所有商品和数量
            redis_cart = redis_conn.hgetall('carts_%s' % user_id)
            # 查询set中的勾选状态
            redis_selected = redis_conn.smembers('selected_%s' % user_id)

            # 将redis购物车转成可操作对象:将redis_cart和redis_selected里面的数据合并到一个购物车字典中
            # 使用redis_cart和redis_selected构造一个跟cookie购物车字典一样的字典
            cart_dict = {}
            for sku_id, count in redis_cart.items():
                cart_dict[int(sku_id)] = {
                    'count': int(count),
                    'selected': sku_id in redis_selected,
                }
        # 未登录,查询cookie购物车
        else:
            # 从cookie中读取购物车字典
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                cart_dict = {}

        # 读取购物车里面商品信息
        sku_ids = cart_dict.keys()
        sku_model_list = SKU.objects.filter(id__in=sku_ids)

        cart_skus = []
        for sku in sku_model_list:
            cart_skus.append({
                "id": sku.id,
                "name": sku.name,
                "default_image_url": sku.default_image.url,
                "price": sku.price,
                "count": cart_dict[sku.id]['count'],
                "selected": cart_dict[sku.id]['selected'],
                "amount": sku.price * cart_dict[sku.id]['count'],

            })

        # 响应结果
        return http.JsonResponse({"code": 0, 'errmsg': 'OK', 'cart_skus': cart_skus})

    def put(self, request):
        """修改购物车"""
        # 接收参数
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')
        count = json_dict.get('count')
        # 如果前端没有传递selected参数,我们给默认值True,即默认勾选
        selected = json_dict.get('selected', True)

        # 校验参数
        if not all([sku_id, count]):
            return http.JsonResponse({"code": 400, 'errmsg': '缺少比传参数'})
        try:
            SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return http.JsonResponse({"code": 400, 'errmsg': '参数sku_id错误'})
        try:
            # 使用int类型强转count,如果count不是数字,会抛出异常
            count = int(count)
        except BaseException as e:
            return http.JsonResponse({"code": 400, 'errmsg': '参数count错误'})
        # 校验selected参数
        if selected:
            if not isinstance(selected, bool):
                return http.JsonResponse({"code": 400, 'errmsg': '参数selected错误'})

        # 实现逻辑
        # 判断用户是否登录
        if request.user.is_authenticated:
            # 已登录,修改redis购物车,覆盖写入
            # 由于前端向后端发送的是修改后的数据,所以后端直接覆盖写入即可
            user_id = request.user.id
            redis_conn = get_redis_connection('carts')
            pl = redis_conn.pipeline()
            pl.hset('carts_%s' % user_id, sku_id, count)
            if selected:
                pl.sadd('selected_%s' % user_id, sku_id)
            else:
                pl.srem('selected_%s' % user_id, sku_id)
            pl.execute()

            # 在修改成功后,记得将修改后的数据返回给前端,实现局部刷新效果
            cart_sku = {
                "id": sku_id,
                "count": count,
                "selected": selected,
            }

            # 响应结果,实现局部刷新
            return http.JsonResponse({"code": 0, 'errmsg': 'OK', 'cart_sku': cart_sku})
        # 未登录,修改cookie购物车
        else:
            # 由于前端向后端发送的是修改后的数据,所以后端直接覆盖写入即可
            # 从cookie中读取购物车字典
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                cart_dict = {}
            # 修改购物车字典
            cart_dict[sku_id] = {
                'count': count,
                "selected": selected,
            }
            # 将购物车字典转字符串并写入到cookie
            cookie_cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
            # 构造响应数据
            cart_sku = {
                'id': sku_id,
                'count': count,
                'selected': selected,
            }
            response = http.JsonResponse({'code': 0, 'errmsg': 'OK', 'cart_sku': cart_sku})
            response.set_cookie('carts', cookie_cart_str)

            # 响应结果,实现局部刷新
            return response

    def delete(self, request):
        """删除购物车"""
        # 接收参数
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')

        # 校验参数
        try:
            SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return http.JsonResponse({'code': 400, 'errmsg': '参数sku_id错误'})

        # 判断用户登录状态
        if request.user.is_authenticated:
            # 已登录删除redis购物车
            user_id = request.user.id
            # 创建连接到redis的对象
            redis_conn = get_redis_connection('carts')
            pl = redis_conn.pipeline()
            # 删除hash中的购物车数据
            pl.hdel('carts_%s' % user_id, sku_id)
            # 删除set中的勾选状态
            pl.srem('selected_%s' % user_id, sku_id)
            pl.execute()

            return http.JsonResponse({'code': 0, 'errmsg': 'OK'})
        # 未登录,删除cookie购物车
        else:
            # 从cookie中读取购物车字典
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                cart_dict = {}
            # 删除购物车字典中的key
            # 注意点:在删除字典时,必须判断字典的key是否存在,只能删除已存在的key,否则报错
            if sku_id in cart_dict:
                del cart_dict[sku_id]

            # 将购物车字典转字符串并写入到cookie
            cookie_cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()

            response = http.JsonResponse({'code': 0, 'errmsg': 'OK'})
            response.set_cookie('carts', cookie_cart_str)

            # 响应结果
            return response


class CartSelectAllView(View):
    """购物车全选"""
    def put(self, request):
        # 接收参数
        dict = json.loads(request.body.decode())
        selected = dict.get('selected')

        # 校验参数
        if selected:
            if not isinstance(selected, bool):
                return http.JsonResponse({'code': 400, 'errmsg': 'selected参素有误'})

        # 判断用户登录状态
        if request.user.is_authenticated:
            # 已登录,全选redis购物车
            # 创建连接到redis的对象
            redis_conn = get_redis_connection('carts')
            # 读取hash中所有的sku_id
            item_dict = redis_conn.hgetall('carts_%s' % request.user.id)
            # 确定全选或取消全选
            # 读取字典中所有的sku_id
            sku_ids = item_dict.keys()

            if selected:
                # 确定全选:将sku_ids中所有的sku_id添加到set
                redis_conn.sadd('selected_%s' % request.user.id, *sku_ids)
            else:
                # 取消全选:将sku_ids中所有的sku_id从set中移除
                redis_conn.srem('selected_%s' % request.user.id, *sku_ids)

            return http.JsonResponse({'code': 0, 'errmsg': 'OK'})
        # 未登录,全选cookie购物车
        else:
            # 从cookie中读取购物车字典
            cookie_cart = request.COOKIES.get('carts')
            response = http.JsonResponse({'code': 0, 'errmsg': 'OK'})
            # 将购物车字典中的所有selected字段设置为True或False
            if cookie_cart:
                cart_dict = pickle.loads(base64.b64decode(cookie_cart))
                for sku_id in cart_dict.keys():
                    cart_dict[sku_id]['selected'] = selected
                cart_data = base64.b64encode(pickle.dumps(cart_dict)).decode()

                # 将购物车字典转字符串并写入cookie
                response.set_cookie('carts', cart_data)

            # 返回响应
            return response


class SimpleCartsView(View):

    def get(self, request):
        '''返回简易购物车功能'''
        # 1.判断用户是否登录
        user = request.user
        if user.is_authenticated:
            # 2.如果用户登录: 链接redis, 获取链接对象
            redis_conn = get_redis_connection('carts')

            # 3.从hash中取出对应的数据
            # item_dict: {sku_id1 : count1, sku_id2:count2, ...}
            item_dict = redis_conn.hgetall('carts_%s' % user.id)

            # 4.从set中取出对应的数据 :
            # selected_carts:  {sku_id1, sku_id2, ...}
            selected_carts = redis_conn.smembers('selected_%s' % user.id)

            cart_dict = {}
            # 5.把hash + set中有用的数据提取出来放入一个dict中,该dict格式和cookie中dict一样
            for sku_id, count in item_dict.items():
                cart_dict[int(sku_id)] = {
                    'count': int(count),
                    'selected': sku_id in selected_carts
                }
        else:
            # 6.如果用户未登录: 从cookie中取出保存的数据
            cookie_cart = request.COOKIES.get('carts')

            # 7.判断该数据是否存在, 如果该数据存在 ----> 解密 ----> 得到dict
            if cookie_cart:
                cart_dict = pickle.loads(base64.b64decode(cookie_cart.encode()))
            else:
                # 8.如果该数据不存在 -----> 创建新的dict
                cart_dict = {}

        # 9.统一处理:
        # {
        #     sku_id : {
        #         'count':count,
        #         'selected':True/False
        #     }
        # }
        # 10.从dict中获取该dict的所有key值 ===> sku_ids
        cart_skus = []
        sku_ids = cart_dict.keys()

        # 11.把sku_ids转为skus 因为filter返回查询集,而查询集有缓存;但是,提交订单数据时是实时更新库存和销量的,此时库存和销量不能为缓存数据
        # 查询SKU信息
        try:
            skus = SKU.objects.filter(id__in=sku_ids)
        except Exception as e:
            return http.JsonResponse({'code': 400,
                                 'errmsg': '获取商品数据出错'})

        # 12.遍历skus, 获取每一个sku
        for sku in skus:
            # 13.把sku的相关属性存入 ----> {}  ----> []
            cart_skus.append({
                'id': sku.id,
                'name': sku.name,
                'count': cart_dict.get(sku.id).get('count'),
                'default_image_url': sku.default_image.url,
            })

        # 14.把[]转为json格式, 返回
        return http.JsonResponse({'code': 0,
                             'errmsg': 'OK',
                             'cart_skus': cart_skus})

